package espaceVisuCarre;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Stack;
import java.util.Vector;

import javax.swing.JCheckBox;
import javax.xml.soap.Text;

import xmlItunes.Bibliotheque;
import xmlItunes.Track;
import espaceVisuCarre.java2Dutil.CircleDrawable;
import espaceVisuCarre.java2Dutil.GraphicListener;
import espaceVisuCarre.java2Dutil.IDrawable;
import espaceVisuCarre.java2Dutil.JCanvas;
import espaceVisuCarre.java2Dutil.RectangleDrawable;
import espaceVisuCarre.java2Dutil.TextDrawable;

/**
 * Classe de gestion de l'espace des carres
 */
public class Espace implements ComponentListener , GraphicListener
{
	
	private boolean afficherLegende ;
	
	private Bibliotheque bib ;
	private LinkedList<Track> tracks;
	
	private JCanvas jc ;
	
	private String critereLargeur			=	"Genre"	;
	private LinkedList<Carre> carres ;
	
	private String critereHauteur			=	"Year"	;
	private LinkedList<SectionDeCarre> sections ;
	
	private String critereDeCouleurTrack	= 	"Artist";


	/**
	 * constructeur
	 * @param b la biliotehque a afficher
	 * @param canv le panel qui contiendra les formes a afficher
	 */
	public Espace(Bibliotheque b, JCanvas canv)
	{
		bib = b ;
		jc = canv ;
		carres = new LinkedList<Carre>();
		sections = new LinkedList<SectionDeCarre>();		
		
		this.tracks = new LinkedList<Track>( bib.getTracks().values() );
		
		afficherLegende = false ;
		
		this.construire();
	}

	/**
	 *  construction : ajout des listener , ... 
	 */
	private void construire()
	{
		construireGrandCarre();
		
		jc.setBackground(Color.WHITE);
		
		//Dimension oldSize = new Dimension(500, 500);
		//jc.setPreferredSize(oldSize);
		

		jc.addComponentListener(this);
		jc.addListener(this);
		
		dessiner();
	}
	
	/**
	 * construction des carrés : tri des mp3 selon les criteres
	 */
	private void construireGrandCarre()
	{		
		HashMap<String 		, Vector<Track> > typesLargeur = new HashMap<String 	, Vector<Track> >();
		HashMap<BigInteger 	, Vector<Track> > typesHauteur = new HashMap<BigInteger , Vector<Track> >();
		
		// compter les valeurs differentes
		for( Track t : tracks )
		{
			String valueLargeur = t.getAttribute(critereLargeur);
			if(valueLargeur==null)valueLargeur="null";
			{
				Vector<Track> v = typesLargeur.get( valueLargeur );
				if(  v == null )
				{
					v = new Vector<Track>();
					typesLargeur.put(valueLargeur, v );
				}
				v.add(t);
			}
			
			
			String tmp =  t.getAttribute(critereHauteur);
			BigInteger valueHauteur = null ;
			if( tmp == null )tmp="-1";
			{
				try
				{
					valueHauteur = new BigInteger( tmp );
				}
				catch(java.lang.NumberFormatException nfe){ System.out.println("le champ '"+critereHauteur+"' n'est pas un nb : "+nfe);valueHauteur= null ; }//new BigInteger("0");}
			}
			if( valueHauteur != null )
			{
				Vector<Track> v = typesHauteur.get( valueHauteur ) ;
				if(  v == null )
				{
					v = new Vector<Track>();
					typesHauteur.put(valueHauteur, v );	
				}
				v.add(t);
			}
			
			
			
			
		}
		
		
		
		/* construction des carres */
		if(carres != null)		carres.clear()		;else carres	= new LinkedList<Carre>();
		if(sections != null )	sections.clear()	;else sections 	= new LinkedList<SectionDeCarre>();		
		
		
		
		//System.out.println("largeur : ");
		Carre carre = new Carre();
		for( String s : typesLargeur.keySet() )
		{
			carre.getValues().add(s);
			
			carre.getTracks().addAll( typesLargeur.get(s));

			//if( carre.getTracks().size() >= bib.size() / 20 )
			{
				float proport = (float)carre.getTracks().size() /  (float)tracks.size() ;
				carre.setProportionLargeur(proport);
				
				carres.add( carre );
				
				carre = new Carre( );
			}
			
		}
		
		
		List<BigInteger> triee = new ArrayList<BigInteger>( typesHauteur.keySet() ) ;
		//for( SectionDeCarre key : this.sections ){ triee.add( new BigInteger( key.getNom()) ) ; }
		java.util.Collections.sort(triee);
		
		//System.out.println( triee.get(0) +" - " +  triee.get( triee.size()-1));

		
		SectionDeCarre sec= new SectionDeCarre();
		for( BigInteger s : triee )
		{			
			sec.getValues().add(s+"");
			sec.getTracks().addAll( typesHauteur.get(s) );
			
			//if( sec.getTracks().size() >= bib.size() / 20 )
			{				
				float proport = (float)sec.getTracks().size() /  (float)tracks.size() ;
				sec.setProportion(proport);
				
				sections.add( sec );
				sec = new SectionDeCarre();
			}

		}

		/*
		for( Carre c : carres )
		{
			System.out.println(c.getNom()+" -> "+c.getTracks().size() );
		}
		
		for( SectionDeCarre s : sections )
		{
			System.out.println(s.getNom()+" -> "+s.getTracks().size() );
		}
		*/
		
		//System.out.println("total hauteur = "+cpt+" >> "+cptf);cpt=0;cptf=0;
			
	}
	
	



	/**
	 * permet de dessiner les carrés qui ont été calculés,sur l'espace de dessin.
	 */
	private void dessiner()
	{
		//if(1==1)return;
		
		double widthMargin = 60 ;
		double heightMargin = 30 ;
		double hauteur = jc.getSize().getHeight() -(heightMargin*2);
		double largeur = jc.getSize().getWidth()  -(widthMargin*2) ;
		
		
		
		float widthOffset = (float)widthMargin ;
		
		boolean verticalLegende = false;
		for( Carre carre : this.carres )
		{
			//System.out.println("dessin du carre "+carre.getNom());
           
			float tailleLargeur = carre.getProportionLargeur() * (float)largeur ; 			
			
            TextDrawable l = new TextDrawable(Color.black , new Rectangle2D.Float((float)widthOffset,(float)0, (float)tailleLargeur , (float)heightMargin ) , carre.getNom() );
            l.setTracks(carre.getTracks());
            jc.addDrawable(l);
            //System.out.println("horizontal "+l.getTexte());
			
            
			float heightOffset = (float)heightMargin ;
			for(SectionDeCarre sec : this.sections )
			{
				String nomSection = sec.getNom();
				float tailleHauteur = sec.getProportion() * (float)hauteur ;
				
				Point2D.Float centreDuRectangle = new Point2D.Float( widthOffset +  (tailleLargeur/2) , heightOffset +  (tailleHauteur/2)  );				
				
				// carre de couleur
				RectangleDrawable r		= new RectangleDrawable(	carre.getCouleur() ,
																	//new  Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255) ),
																	centreDuRectangle,
																	tailleLargeur,
																	tailleHauteur ,
																	true,
																	carre ,
																	sec
																);
				jc.addDrawable(r);
				
				
				if(!verticalLegende)
				{
					TextDrawable labelVertical = 	new TextDrawable(Color.black , new Rectangle2D.Float((float)0,(float)(heightOffset ), (float)widthMargin , (float)tailleHauteur ) , nomSection+""  );
					labelVertical.setTracks(sec.getTracks());
					jc.addDrawable(labelVertical);
					//System.out.println("vertical "+labelVertical.getTexte());
				}
                
				
				
				Vector<Track> liste =new Vector<Track>();
				for(Track t : sec.getTracks() )
				{
					String valeurLargeur = t.getAttribute(critereLargeur) ;
					if(valeurLargeur == null )valeurLargeur = "null";
					
						if( carre.getValues().contains(valeurLargeur) )
						{
							liste.add(t);
						}
					
					
				}
				r.setTracks(liste);
				dessinerTracks(r);
				

				heightOffset += tailleHauteur ;
			}
			
			verticalLegende = true ;
			
			widthOffset +=  tailleLargeur ;

		}
		
		
		// si il y a d'autre bibliotheques dans la pile on doit pouvir revenir en arriere 
		/*
		if( this.pileDeBibliotheque.size() > 1 )
		{
			LinkedList<Track> bibPrecedente = this.pileDeBibliotheque.elementAt( this.pileDeBibliotheque.size()-2 );
			
			
            TextDrawable l = new TextDrawable(	
            									Color.black ,
            									new Rectangle2D.Float((float)jc.getSize().getWidth()/4,(float)(jc.getSize().getHeight()-(heightMargin)), (float)jc.getSize().getWidth()/2 , (float)heightMargin ) ,
            									"retour"
            								);
            l.setTracks( bibPrecedente );
            jc.addDrawable(l);

		}
		*/
	}
	
 
	/**
	 * permet de determiner la couleur des pistes
	 * @param t le track dont on vuet la couleur
	 * @return la couleur
	 */
	private Color trackToColor(Track t)
	{

		String nom =  t.getAttribute(critereDeCouleurTrack);;
		if(nom==null)return Color.black;
		
		nom = nom.toLowerCase();
		
		double r=0;
		double g=0;
		double b=0;
	
		for( int i =0 ; i<nom.length() ; i++ )
		{
			int nv = (int) nom.charAt(i) ;
			
			if(nv > 97  && nv< 105  ){ r++ ;}
			if(nv > 105 && nv< 113  ){ g++ ;}
			if(nv > 113 && nv< 122  ){ b++ ;}
			
		}
		
		double d = (r+g+b);
		r = r / d ;
		g = g / d ;
		b = b / d ;
		
		//System.out.println(r+"/"+g+"/"+b);
		
		Color couleur = new Color( (int)(r*254) ,(int)(g*254), (int)(b*254) ) ;
		return couleur ;
	}
	
	
	/**
	 * 
	 * permet de dessiner les pistes d'un rectangle
	 * 
	 * @param r le rectangle qui contient les pistes a dessiner
	 */
	private void dessinerTracks(RectangleDrawable r	)
	{
		//if(1==1)return ;
		
		
		LinkedList<Track> liste = new LinkedList<Track>(r.getTracks());
		
		HashMap<String , Vector<Track> > tri = new HashMap<String,   Vector<Track> >();
		
		
		// tri des tracks
		for(Track t : liste)
		{
			Vector<Track> v = tri.get( t.getAttribute(critereDeCouleurTrack) );
			if( v == null ){v = new Vector<Track>();}
			v.add(t);
			tri.put(t.getAttribute(critereDeCouleurTrack), v);
		}
		
		// division de l'espace 'x' 
		float widthOffset = 0 ;
		for(String clef : tri.keySet())
		{
			Vector<Track> v = tri.get(clef) ;
			float largeurPartition = ( (float)v.size() / (float)liste.size() ) * r.getRectangle().width; 
			
			if(largeurPartition > 0)
			{
				//System.out.println(clef+" , avec largeur = "+largeurPartition+" = "+v.size()+" / "+liste.size());
				
				float xdebut = widthOffset + r.getRectangle().x  ;
				float ydebut = r.getRectangle().y  ;
				
				
				/*
				float xfin = xdebut + largeurPartition ;
				float yfin = ydebut +  r.getRectangle().height ;
				
				System.out.println("[ "+xdebut+","+xfin+"]["+ydebut+","+yfin+"] ");
				Point2D.Float centr = new Point2D.Float( (xdebut+xfin)/2   , (ydebut+yfin)/2 );
				RectangleDrawable ce = new RectangleDrawable(Color.blue ,centr, xfin-xdebut , yfin-ydebut ,false  , null,null);
				jc.addDrawable(ce);
				*/
				
				int racine = (int)Math.sqrt(v.size());while( racine*racine < v.size() ){ racine++; }
				
				
				int trackCpt = 0;
				for( float haut = 0 ; haut < racine ; haut++ )
				{
					float y = ydebut + haut* ( r.getRectangle().height / racine );
					
					for( float larg = 0 ; larg<racine;larg++ )
					{
						float x = xdebut + larg*( largeurPartition / racine );
						
						
						//System.out.println("("+x+","+y+")");
						
						if( trackCpt < v.size() )
						{
							Track t = v.elementAt(trackCpt);trackCpt++;
							Vector<Track> tr = new Vector<Track>();tr.add(t);
							Point2D.Float centre = new Point2D.Float(x +( largeurPartition / (2*racine) )  ,y+( r.getRectangle().height / (2*racine) ) );
							
							CircleDrawable c = new CircleDrawable( trackToColor(t) ,centre, 5/*( largeurPartition / racine )*/ , 5/*( r.getRectangle().height / racine )*/ ,true );
							c.setTracks(tr);
							
							jc.addDrawable(c);
							
							
							// legende 
							if( this.afficherLegende )
							{
							float largeurLegende = Math.min(( largeurPartition / racine ), 100 );
							float hauteurLegende = Math.min(( r.getRectangle().height / (2*racine) ) , 30 ) ; 
							Rectangle2D.Float rect = new Rectangle2D.Float(centre.x-(largeurLegende/2)  ,centre.y , largeurLegende , hauteurLegende );
							TextDrawable legende = new TextDrawable( Color.black, rect , t.getAttribute("Name") );
							legende.setTracks(tr);
							jc.addDrawable(legende);
							}
						}
						
					}
				}
				
			}
			widthOffset += largeurPartition ;
		}
		
		//System.out.println(widthOffset+" // "+r.getRectangle().width);
		
		return ;
	}
	

	
	@Override
	public void componentHidden(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void componentMoved(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void componentResized(ComponentEvent e)
    {
        //Dimension newSize = jc.getSize();
        jc.clear();
        dessiner();
    }


	@Override
	public void componentShown(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void mouseClicked(IDrawable e) 
	{
		if(e.getTracks().size()>0)
		{
			tracks = new LinkedList<Track>(e.getTracks());
			this.construireGrandCarre();
			
			jc.clear();
			this.dessiner();
		}
	}

	@Override
	/**
	 * methode appellée par l'interface , qd l'utlisateur selectionne les genres ou modifie les valeurs de selectionneur d'annes
	 */
	public void selectionGenreAnnee(HashMap<String, JCheckBox> listeCheckBoxGenres , int min, int max) 
	{
		BigInteger mini= new BigInteger(min+"");
		BigInteger maxi= new BigInteger(max+"");
		
		LinkedList<String> genreOK = new LinkedList<String>();
		for( String clef : listeCheckBoxGenres.keySet() )
    	{
    		if(listeCheckBoxGenres.get(clef).isSelected()) genreOK.add( clef );
    	}
		
		LinkedList<Track> nouvelleBib = new LinkedList<Track>();
		for( Track t :  this.bib.getTracks().values() )
		{
			String y = t.getAttribute("Year") ;if(y==null){y="-1";}
			String g = t.getAttribute("Genre");
			
			BigInteger b = new BigInteger(y);
			
			if( b.compareTo(maxi)<=0 && b.compareTo(mini)>=0 && genreOK.contains( g  )) 
			{
				nouvelleBib.add(t);
			}
		}
		
		if( nouvelleBib.size() > 0 )
		{
			this.tracks = nouvelleBib ; 
			
			this.construireGrandCarre();
			jc.clear();
			this.dessiner();
		}
		else
		{
			jc.clear();
		}
	}


	public void setCritereLargeur(String critereLargeur) 
	{
		this.critereLargeur = critereLargeur;
		this.construireGrandCarre();
		jc.clear();
		this.dessiner();
	}


	public void setCritereHauteur(String critereHauteur)
	{
		this.critereHauteur = critereHauteur;
		this.construireGrandCarre();
		jc.clear();
		this.dessiner();
	}


	public void setAfficherLegende(boolean afficherLegende) 
	{
		this.afficherLegende = afficherLegende;
		jc.clear();
		this.dessiner();		
	}


 
	
}
